package projet.metier;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;

import edu.uci.ics.jung.graph.Graph;

/**
 * Proximity's algorithm. Used for calculate the node's proximity.
 * @author Ambroise
 */
public class AlgorithmProximity {
	
	/**
	 * Get the node's proximity.
	 * 
	 * @param graph - Graph - The graph.
	 * @param node - String - The node's name for which we calculate the proximity.
	 * 
	 * @return int - The value of proximity for the current node.
	 */
	public static float GetProximityByNode(Graph<String, Integer> graph, String node){
		
		// Get the number of vertex.
		float nbNodes = graph.getVertexCount();
		float sumEdges = 0;
		
		// Use Dijkstra for calculate the sum of edges into the node and all others nodes. 
		HashMap<String, Object[]> resultsDijkstra= DijkstraAlgorithm.Dijkstra(graph, node);
		for (String vertex : graph.getVertices()){
			Object[] o = resultsDijkstra.get(vertex);
			// For each edge, add this proximity at the sum.
			sumEdges += (int)o[0];
		}
		
		// Protect divide by zero.
		if(sumEdges == 0){
			return 0;
		}
		
		return ((nbNodes - 1)/(sumEdges));
	}
	
	/**
	 * Get all node's proximity in a HashMap.
	 * 
	 * @param graph - Graph - The graph.
	 * 
	 * @return HashMap<String, Float> - The values of proximities for the graph, indexed by vertex's name.
	 */
	public static HashMap<String, Float> GetProximitysByGraph(Graph<String, Integer> graph){
		HashMap<String, Float> results = new HashMap<String, Float>();
		Collection<String> nodes = graph.getVertices();
		
		for(String nameNode: nodes){
			results.put(nameNode, AlgorithmProximity.GetProximityByNode(graph, nameNode));
		}
		
		return results;
	}
	
	/**
	 * Found the max proximity of the graph pre-calculated.
	 * 
	 * @param graphProximities - HashMap<String, Float> - The graph's proximities. (Pre-calculated)
	 * @param deleteActualMaxValue - Boolean - If true, delete the max value and return the second. Used if the max value cannot be used for the application. (The name of the vertex is already used, for example.)
	 * 
	 * @return String - The key of the vertex with the max proximity.
	 */
	public static String FoundMaxProximity(HashMap<String, Float> graphProximities, Boolean deleteActualMaxValue){
		// Name of the actual key with the max value.
		String maxProximityKey = null;
		
		// Max value found.
		Float maxProximityValue = null;
		
		Set<Entry<String, Float>> entrySet = graphProximities.entrySet();

		Iterator<Entry<String, Float>> it = entrySet.iterator();

		// For each key of the iterator.
		while (it.hasNext()) {
			// Get the actual entry.
			Entry<String, Float> entry = it.next();
			
			// If the minProximityValue isn't initialized, initialize this.
			if(maxProximityValue == null){
				maxProximityValue = entry.getValue();
				maxProximityKey = entry.getKey();
			}
			
			// If the actual value is greater of the max value found.
			if(entry.getValue() > maxProximityValue){
				// Store the key and the value.
				maxProximityKey = entry.getKey();
				maxProximityValue = entry.getValue();
			}
		}
		
		// If it's asked to delete the max value, delete and recalculate.
		if(deleteActualMaxValue){
			graphProximities.remove(maxProximityKey);
			return AlgorithmProximity.FoundMaxProximity(graphProximities, false);
		}else{
			// Put the key => value.
			return maxProximityKey;
		}
	}
	
	/**
	 * Found the min proximity of the graph pre-calculated.
	 * 
	 * @param graphProximities - HashMap<String, Float> - The graph's proximities. (Pre-calculated)
	 * @param deleteActualMinValue - Boolean - If true, delete the min value and return the second. Used if the min value cannot be used for the application. (The name of the vertex is already used, for example.)
	 * 
	 * @return String - The key of the vertex with the min proximity.
	 */
	public static String FoundMinProximity(HashMap<String, Float> graphProximities, Boolean deleteActualMinValue){
		// Name of the actual key with the max value.
		String minProximityKey = null;
		
		// Max value found.
		Float minProximityValue = null;
		
		Set<Entry<String, Float>> entrySet = graphProximities.entrySet();

		Iterator<Entry<String, Float>> it = entrySet.iterator();

		// For each key of the iterator.
		while (it.hasNext()) {
			
			
			// Get the actual entry.
			Entry<String, Float> entry = it.next();
			
			// If the minProximityValue isn't initialized, initialize this.
			if(minProximityValue == null){
				minProximityValue = entry.getValue();
				minProximityKey = entry.getKey();
			}
			
			// If the actual value is greater of the max value found.
			if(entry.getValue() < minProximityValue){
				// Store the key and the value.
				minProximityKey = entry.getKey();
				minProximityValue = entry.getValue();
			}
		}
		
		// If it's asked to delete the max value, delete and recalculate.
		if(deleteActualMinValue){
			graphProximities.remove(minProximityKey);
			return AlgorithmProximity.FoundMinProximity(graphProximities, false);
		}else{
			// Put the key => value.
			return minProximityKey;
		}
	}
	
	/**
	 * Return the key name of the max proximity for a graph. Call GetProximitysByGraph and FoundMaxProximity.
	 * 
	 * @param graph - Graph<String, Integer> - The graph.
	 * @param deleteActualMaxValue - Boolean - If true, delete the max value and return the second. Used if the max value cannot be used for the application. (The name of the vertex is already used, for example.)
	 * 
	 * @return String - The key name of the max value found.
	 */
	public static String GetMaxProximityByGraph(Graph<String, Integer> graph, Boolean deleteActualMaxValue){
		return AlgorithmProximity.FoundMaxProximity(AlgorithmProximity.GetProximitysByGraph(graph), deleteActualMaxValue);
	}
	
	/**
	 * Return the key name of the min proximity for a graph. Call GetProximitysByGraph and FoundMinProximity.
	 * 
	 * @param graph - Graph<String, Integer> - The graph.
	 * @param deleteActualMinValue - Boolean - If true, delete the min value and return the second. Used if the min value cannot be used for the application. (The name of the vertex is already used, for example.)
	 * @return String - The key name of the min value found.
	 */
	public static String GetMinProximityByGraph(Graph<String, Integer> graph, Boolean deleteActualMinValue){
		return AlgorithmProximity.FoundMinProximity(AlgorithmProximity.GetProximitysByGraph(graph), deleteActualMinValue);
	}
	
}
